use std::fs::{self, File};
use std::io::{BufWriter, Write};
use std::path::PathBuf;
use std::{io, path::Path};

use openssl::bn::BigNumContext;
use openssl::hash::MessageDigest;
use openssl::x509::{X509NameBuilder, X509};
use openssl::{
    asn1::{Asn1Integer, Asn1Time},
    bn::BigNum,
    pkey::PKey,
    rsa::Rsa,
    x509::X509Builder,
};

mod root_certificate;

/// 生成证书，公钥保存为.pem文件，私钥保存为.key文件
///
/// - `storage_path`：证书存储路径
/// - `certificate_name`：证书名称
/// - `key_length`：密钥长度
/// - `available_days`：证书有效天数
/// - `version`：证书版本
/// - `serial_number`：证书序列号，如果不设定则默认为1
pub fn generate_certificate(
    storage_path: &str,
    certificate_name: &str,
    key_length: u32,
    available_days: u32,
    version: i32,
    serial_number: Option<u32>,
) -> anyhow::Result<()> {
    let rsa = Rsa::generate(key_length)?;
    let private_key = rsa.private_key_to_pem()?;
    let pkey = PKey::from_rsa(rsa)?;
    let mut builder = X509Builder::new()?;
    builder.set_pubkey(&pkey)?;

    let mut x509_name = X509NameBuilder::new()?;
    x509_name.append_entry_by_text("CN", certificate_name)?;
    let x509_name = x509_name.build();
    let mut x509_issuer = X509NameBuilder::new()?;
    x509_issuer.append_entry_by_text("CN", "JetProfile CA")?;
    let x509_issuer = x509_issuer.build();
    builder.set_subject_name(&x509_name)?;
    builder.set_issuer_name(&x509_issuer)?;
    builder.set_version(version)?;

    let not_before = Asn1Time::days_from_now(0)?;
    let not_after = Asn1Time::days_from_now(available_days)?;
    builder.set_not_before(&not_before)?;
    builder.set_not_after(&not_after)?;
    let serial_number = BigNum::from_u32(serial_number.unwrap_or(1))?;
    let sn = Asn1Integer::from_bn(&serial_number)?;
    builder.set_serial_number(&sn)?;

    builder.sign(&pkey, MessageDigest::sha256())?;

    let certificate = builder.build();

    let store_path = PathBuf::from(storage_path);
    ensure_path_exists(&store_path).unwrap();

    let cert_path = store_path.clone().join(format!("{}.pem", certificate_name));
    let cert_file = File::create(cert_path)?;
    let mut writer = BufWriter::new(cert_file);
    let cert_data = certificate.to_pem()?;
    writer.write_all(&cert_data)?;
    writer.flush()?;

    let private_key_path = store_path.join(format!("{}.key", certificate_name));
    let private_key_file = File::create(private_key_path)?;
    let mut writer = BufWriter::new(private_key_file);
    writer.write_all(&private_key)?;
    writer.flush()?;

    Ok(())
}

/// 确保指定路径存在，如果不存在则创建
///
/// - `target_path`：目标路径
fn ensure_path_exists<P: AsRef<Path>>(target_path: P) -> Result<(), io::Error> {
    let path = target_path.as_ref();
    if !path.exists() {
        fs::create_dir_all(path)?;
    }

    Ok(())
}

/// 计算用于net-filter中power插件中Equal所使用的值。
///
/// - `cert`：用于生成EQUAL值的X509证书
pub fn calculate_power_euqal_result(cert: X509) -> anyhow::Result<String> {
    let x = BigNum::from_slice(cert.signature().as_slice())?;
    let y = BigNum::from_u32(65537)?;
    let root_cert_pub_key = root_certificate::x509_certificate()?.public_key()?.rsa()?;
    let z = root_cert_pub_key.n();
    let cert_pub_key = cert.public_key()?.rsa()?;
    let n = cert_pub_key.n();

    let mut ctx = BigNumContext::new()?;
    let mut result = BigNum::new()?;
    result.mod_exp(&x, &y, &n, &mut ctx)?;
    Ok(format!(
        "EQUAL,{},{},{}->{}",
        x.to_dec_str()?,
        y.to_dec_str()?,
        z.to_dec_str()?,
        result.to_dec_str()?
    ))
}

/// 从.pem文件中读取X509证书
///
/// - `file_path`：证书文件路径
pub fn load_certificate<P: AsRef<Path>>(file_path: P) -> anyhow::Result<X509> {
    let file_path = file_path.as_ref();
    let cert_pem = fs::read(file_path)?;
    let cert = X509::from_pem(&cert_pem)?;
    Ok(cert)
}

/// 从.key文件中读取RSA私钥
///
/// - `file_path`：私钥文件路径
pub fn load_private_key<R: AsRef<Path>>(
    file_path: R,
) -> anyhow::Result<PKey<openssl::pkey::Private>> {
    let file_path = file_path.as_ref();
    let key_pem = fs::read(file_path)?;
    let key = PKey::private_key_from_pem(&key_pem)?;
    Ok(key)
}
